﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;



namespace WindowsGame
{
    public interface ICamera
    {
        Matrix ViewMatrix { get; }
        Vector3 Position { get; }
    }



    public class Camera : ICamera
    {
        const float TRANSLATION_STEP = 100.0f;
        const float ROTATION_STEP = 1.0f;

        public Matrix viewMatrix;
        Vector3 position = new Vector3(0.0f, 50.0f, 0.0f);
        Vector3 forward = new Vector3(0.0f, -1.0f, 0.0f);
        Vector3 right = new Vector3(1.0f, 0.0f, 0.0f);
        Vector3 up = new Vector3(0.0f, 0.0f, -1.0f);

        bool rebuildNeeded = true;

		public Matrix ViewMatrix { get { return viewMatrix; } }
		public Vector3 Position { get { return position; } }



        public Camera()
        {
            viewMatrix = Matrix.Identity;
            BuildViewMatrix();
        }



        public void BuildViewMatrix()
        {
            if(rebuildNeeded)
            {
                Vector3.Normalize(ref forward, out forward);
                Vector3.Cross(ref forward, ref up, out right);
                Vector3.Normalize(ref up, out up);
                Vector3.Cross(ref up, ref right, out forward);
                Vector3.Normalize(ref right, out right);

                float x = -Vector3.Dot(position, right);
                float y = -Vector3.Dot(position, up);
                float z = Vector3.Dot(position, forward);

                viewMatrix.M11 = right.X;
                viewMatrix.M21 = right.Y;
                viewMatrix.M31 = right.Z;

                viewMatrix.M12 = up.X;
                viewMatrix.M22 = up.Y;
                viewMatrix.M32 = up.Z;

                viewMatrix.M13 = -forward.X;
                viewMatrix.M23 = -forward.Y;
                viewMatrix.M33 = -forward.Z;

                viewMatrix.Translation = new Vector3(x, y, z);
                rebuildNeeded = false;
            }
        }



        public void MoveForward(float dT)
        {
            position += forward * TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void MoveBackward(float dT)
        {
            position -= forward * TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void MoveZForward(float dT)
        {
            position.Z -= TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void MoveZBackward(float dT)
        {
            position.Z += TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void StrafeLeft(float dT)
        {
            position += right * TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void StrafeRight(float dT)
        {
            position -= right * TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void MoveYUp(float dT)
        {
            position.Y += TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void MoveYDown(float dT)
        {
            position.Y -= TRANSLATION_STEP * dT;
            rebuildNeeded = true;
        }



        public void PitchUp(float dT)
        {
            Matrix mat;
            float rightAngle = ROTATION_STEP * dT;
            Matrix.CreateFromAxisAngle(ref right, rightAngle, out mat);
            Vector3.Transform(ref up, ref mat, out up);
            Vector3.Transform(ref forward, ref mat, out forward);
            rebuildNeeded = true;
        }



        public void PitchDown(float dT)
        {
            Matrix mat;
            float rightAngle = -ROTATION_STEP * dT;
            Matrix.CreateFromAxisAngle(ref right, rightAngle, out mat);
            Vector3.Transform(ref up, ref mat, out up);
            Vector3.Transform(ref forward, ref mat, out forward);
            rebuildNeeded = true;
        }



        public void YawLeft(float dT)
        {
            Matrix mat;
            float upAngle = ROTATION_STEP * dT;
            Matrix.CreateFromAxisAngle(ref up, upAngle, out mat);
            Vector3.Transform(ref forward, ref mat, out forward);
            //Vector3.Transform(ref right, ref mat, out right);
            rebuildNeeded = true;
        }



        public void YawRight(float dT)
        {
            Matrix mat;
            float upAngle = -ROTATION_STEP * dT;
            Matrix.CreateFromAxisAngle(ref up, upAngle, out mat);
            Vector3.Transform(ref forward, ref mat, out forward);
            //Vector3.Transform(ref right, ref mat, out right);
            rebuildNeeded = true;
        }



        public void RollLeft(float dT)
        {
            Matrix mat;
            float forwardAngle = -ROTATION_STEP * dT;
            Matrix.CreateFromAxisAngle(ref forward, forwardAngle, out mat);
            Vector3.Transform(ref up, ref mat, out up);
            //Vector3.Transform(ref right, ref mat, out right);
            rebuildNeeded = true;
        }



        public void RollRight(float dT)
        {
            Matrix mat;
            float forwardAngle = ROTATION_STEP * dT;
            Matrix.CreateFromAxisAngle(ref forward, forwardAngle, out mat);
            Vector3.Transform(ref up, ref mat, out up);
            //Vector3.Transform(ref right, ref mat, out right);
            rebuildNeeded = true;
        }



        public void RotateYLeft(float dT)
        {
            Matrix mat;
            float yAngle = -ROTATION_STEP * dT;
            Matrix.CreateRotationY(yAngle, out mat);
            //Vector3.Transform(ref right, ref mat, out right);
            Vector3.Transform(ref up, ref mat, out up);
            Vector3.Transform(ref forward, ref mat, out forward);
            rebuildNeeded = true;
        }



        public void RotateYRight(float dT)
        {
            Matrix mat;
            float yAngle = ROTATION_STEP * dT;
            Matrix.CreateRotationY(yAngle, out mat);
            //Vector3.Transform(ref right, ref mat, out right);
            Vector3.Transform(ref up, ref mat, out up);
            Vector3.Transform(ref forward, ref mat, out forward);
            rebuildNeeded = true;
        }
    }
}